/* ----------------------------------------------------------------------------
* File: timer_impl.cpp
*
* Desc: Timer class implementation
*
* Created: 15/08/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "timer_impl.h"

#define WAIT_STOP_TIME		5

namespace Common
{
	TimerImpl::TimerImpl(const TimerParams& params) throw(Exception)
		: m_StartEvent(NULL)
		, m_StopEvent(NULL)
	{
		CheckParams(params);
		m_Params = params;

		m_StartEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		if (!m_StartEvent)
			throw Exception("Timer: ERROR -  Unable to create start event");

		m_StopEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		if (!m_StopEvent)
			throw Exception("Timer: ERROR -  Unable to create stop event");

		// Timer thread starts by construct of object
		if (!StartThread())
			throw Exception("Timer: ERROR - Unable to start thread");
	}

	TimerImpl::~TimerImpl()
	{
		// Timer thread stops by destroying object
		StopThread();

		::CloseHandle(m_StartEvent);
		::CloseHandle(m_StopEvent);
	}

	Error TimerImpl::Start()
	{
		::SetEvent(m_StartEvent);
		return SKYPE_SUCCESS;
	}

	Error TimerImpl::Stop()
	{
		::SetEvent(m_StopEvent);
		return SKYPE_SUCCESS;
	}

	Error TimerImpl::Run()
	{
		HANDLE Events[2];
		Events[0] = m_StartEvent;
		Events[1] = m_hEventUserAbort;

		while(!QueryExitStatus(WAIT_STOP_TIME))
		{
			// Wait start event
			DWORD dw = WaitForMultipleObjects(2, (const HANDLE*)Events, FALSE, INFINITE);
			switch(dw)
			{
			case WAIT_OBJECT_0:		// start event
				::ResetEvent(m_StartEvent);
				PeriodicPulse();
				break;

			case WAIT_OBJECT_0 + 1:	// stop user event
				break;

			default:
				break;
			}
		}

		return SKYPE_SUCCESS;
	}

	void TimerImpl::PeriodicPulse()
	{
		HANDLE Events[2];
		Events[0] = m_StopEvent;
		Events[1] = m_hEventUserAbort;

		bool pulse = true;
		while(pulse)
		{
			DWORD dw = WaitForMultipleObjects(2, (const HANDLE*)Events, FALSE, m_Params.TimeOut);
			switch(dw)
			{
			case WAIT_OBJECT_0:			// stop pulse
				::ResetEvent(m_StopEvent);
				pulse = false;
				break;

			case WAIT_OBJECT_0 + 1:		// stop timer
				pulse = false;
				break;

			case WAIT_TIMEOUT:			// pulse
				m_Params.TimeCallback(m_Params.Parent);
				break;
			}
		}
		
	}

	Error TimerImpl::CheckParams(const TimerParams& params)
		throw(Exception)
	{
		if (!params.TimeCallback)
			throw Exception("Timer: ERROR - Incorrect TimeCallback parameter");
		if (!params.Parent)
			throw Exception("Timer: ERROR - Incorrect Parent parameter");

		return SKYPE_SUCCESS;
	}

	std::auto_ptr<Timer> CreateTimer(const TimerParams& params)
	{
		std::auto_ptr<Timer> timer;
		try
		{
			std::auto_ptr<Timer> obj(new TimerImpl(params));
			timer = obj;
		}
		catch(...)
		{
		}
		return timer;
	}
}